package com.dl.test.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author DL
 * @create 2022-12-21 16:00
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Either<L, R> {

    private L left;

    private R right;

    public boolean isLeft(){
        return left != null;
    }

    public boolean isRight(){
        return right != null;
    }

    public static <L, R> Either<L, R> left(L exception){
        Either<L, R> e = new Either<>();
        e.left = exception;
        return e;
    }

    public static <L, R> Either<L, R> right(R value){
        Either<L, R> e = new Either<>();
        e.right = value;
        return e;
    }

    public static Object getResult(Either<?, ?> e){
        if (e.isLeft()){
            return e.getLeft();
        }else {
            return e.getRight();
        }
    }

    public <T> Either<L, T> map(Function<R, T> function){
        if (isLeft()){
            return left(left);
        }else {
            return right(function.apply(right));
        }
    }

    public static <L, R> Either<L, List<R>> sequence(List<Either<L, R>> eitherList, BinaryOperator<L> accumulator) throws Throwable {
        if (eitherList.stream().allMatch(Either::isRight)){
            return right(eitherList.stream().map(Either::getRight).collect(Collectors.toList()));
        }else {
            return left(eitherList.stream()
                    .filter(Either::isLeft)
                    .map(Either::getLeft)
                    .reduce(accumulator)
                    .orElseThrow((Supplier<Throwable>) () -> null));
        }
    }

}
